package pku;

import java.util.*;

/**
 * 一个Key-Value的实现
 */
public class DefaultKeyValue implements KeyValue {
    public final HashMap<String, Object> kvs = new HashMap<>();

    public Object getObj(String key) {
        return kvs.get(key);
    }

    public HashMap<String, Object> getMap(){
        return kvs;
    }

    public DefaultKeyValue put(String key, int value) {
        kvs.put(key, value);
        return this;
    }

    public DefaultKeyValue put(String key, long value) {
        kvs.put(key, value);
        return this;
    }

    public DefaultKeyValue put(String key, double value) {
        kvs.put(key, value);
        return this;
    }

    public DefaultKeyValue put(String key, String value) {
        kvs.put(key, value);
        return this;
    }

    public int getInt(String key) {
        return (Integer) kvs.getOrDefault(key, 0);
    }

    public long getLong(String key) {
        return (Long) kvs.getOrDefault(key, 0L);
    }

    public double getDouble(String key) {
        return (Double) kvs.getOrDefault(key, 0.0d);
    }

    public String getString(String key) {
        return (String) kvs.getOrDefault(key, null);
    }

    public Set<String> keySet() {
        return kvs.keySet();
    }

    public boolean containsKey(String key) {
        return kvs.containsKey(key);
    }

    public byte[] toBytes() {
        List<Byte> headersBytes = new ArrayList<>();
//        Set<String> keys = kvs.keySet();
        List<String> keys = new ArrayList<>(kvs.keySet());
//        int headersCnt = keys.size();
//        headersBytes.add((byte) (headersCnt & 0xff));
        byte[] keyTypeCode = new byte[2];
        keys.sort(Comparator.comparingInt(Utils::getByte));
        for (String key: keys) {
            switch (key){
                //type: int
                case MessageHeader.MESSAGE_ID: keyTypeCode[0] |= 0x01; break;
                case MessageHeader.TIMEOUT: keyTypeCode[0] |= 0x02; break;
                case MessageHeader.PRIORITY: keyTypeCode[0] |= 0x04; break;
                case MessageHeader.RELIABILITY: keyTypeCode[0] |= 0x08; break;
                // type: long
                case MessageHeader.BORN_TIMESTAMP: keyTypeCode[0] |= 0x10; break;
                case MessageHeader.STORE_TIMESTAMP: keyTypeCode[0] |= 0x20; break;
                case MessageHeader.START_TIME: keyTypeCode[0] |= 0x40; break;
                case MessageHeader.STOP_TIME: keyTypeCode[0] |= 0x80; break;
                // type: double
                case MessageHeader.SHARDING_KEY: keyTypeCode[1] |= 0x01; break;
                case MessageHeader.SHARDING_PARTITION: keyTypeCode[1] |= 0x02; break;
                // type: String
                case MessageHeader.TOPIC: keyTypeCode[1] |= 0x04;break;
                case MessageHeader.BORN_HOST: keyTypeCode[1] |= 0x08;break;
                case MessageHeader.STORE_HOST: keyTypeCode[1] |= 0x10;break;
                case MessageHeader.SEARCH_KEY: keyTypeCode[1] |= 0x20;break;
                case MessageHeader.SCHEDULE_EXPRESSION: keyTypeCode[1] |= 0x40;break;
                case MessageHeader.TRACE_ID: keyTypeCode[1] |= 0x80;break;
            }
        }
        headersBytes.add(keyTypeCode[0]);
        headersBytes.add(keyTypeCode[1]);

        for (String key: keys) {
            byte keyType = Utils.getByte(key);
            if (keyType < 0x04) {
                int val = this.getInt(key);
                byte[] valBytes = Utils.int2bytes(val);
                for (byte b: valBytes) headersBytes.add(b);
            } else if (keyType < 0x08) {
                long val = this.getLong(key);
                byte[] valBytes = Utils.long2bytes(val);
                for (byte b: valBytes) headersBytes.add(b);
            } else if (keyType < 0x0a) {
                double val = this.getDouble(key);
                byte[] valBytes = Utils.double2bytes(val);
                for (byte b: valBytes) headersBytes.add(b);
            } else {
                byte[] valBytes = this.getString(key).getBytes();
                int valLen = valBytes.length;
                byte[] valLenBytes = Utils.int2bytes2(valLen);
                for (byte b: valLenBytes) headersBytes.add(b);
                for (byte b: valBytes) headersBytes.add(b);
            }
        }
        int len = headersBytes.size();
        byte[] headers = new byte[len];
        for (int i = 0; i < len; i++) {
            headers[i] = headersBytes.get(i);
        }
        return headers;
    }
}
